package edu.biwu.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@Component
public class TransactionProxyFactory02 {

    @Autowired
    private TransactionManager transactionManager;

    @Autowired
    private ApplicationContext applicationContext; //注入spring容器

    /**
     *
     * @param cls 传递过来被代理类的字节码
     * @return
     * 例如:传递过来ProductServiceImpl.class,将来该方法就返回ProductServiceImpl代理类对象
     */
    @SuppressWarnings("unchecked")
    public Object createProxyInstance(Class cls){
        if (cls.getInterfaces().length != 0) {//判断传递过来的被代理类是否实现接口,如果实现采用JDK动态代理方式
            return Proxy.newProxyInstance(
                    cls.getClassLoader(),//加载当前代理类
                    cls.getInterfaces(),//代理类和被代理类实现的接口

                    (proxy, method, args) -> InvokeWithTransaction(cls,method,args)
            );
        }else {//该类没有实现任何接口,使用CGLIB动态代理 将被代理类作为父类
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(cls);
            enhancer.setCallback(

                    (MethodInterceptor)(object, method, args, proxy) -> InvokeWithTransaction(cls,method,args)
                    //方法内需要什么参数就传递什么参数 上面小括号中的参数要与方法的参数一一对应 但在传参时不一定都传过去或者说 不一定都一样
            );
            return enhancer.create();
        }
    }

    private Object InvokeWithTransaction(Class cls, Method method, Object[] args) {
        Object reval = null;
        //如果通过代理类对象调用的方法是find开头的方法(查询相关)
        if (method.getName().startsWith("find")) {
            //applicationContext.getBean(cls),applicationContext.getBean(cls根据Bean的类型去获取该Bean
            try {
                reval = method.invoke(applicationContext.getBean(cls), args);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        } else {
            //如果通过代理类对象调用的方法不是find开头的方法(查询相关)
            //我们才加入事务控制
            try {
                // productServiceImpl.增删改方法;
                transactionManager.beginTransaction();
                reval = method.invoke(applicationContext.getBean(cls), args);//假如cls=ProductServiceImpl.class
                transactionManager.commitTansaction();
            } catch (Exception e) {
                transactionManager.rollbackTransaction();
                e.printStackTrace();
            } finally {
                transactionManager.releaseConnection();
            }
        }
        return reval;
    }
}
